package ppjava12v1.chess.chat;


import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.json.Json;
import javax.json.JsonObjectBuilder;
import javax.json.JsonReader;
import javax.json.JsonWriter;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Component
@ServerEndpoint("/chat/{nickname}")
public class ChatServerEndpoint {

    /**
     * Константы для описания полей структуры входящего сообщения и
     * описание параметров исходящего сообщения
     * <p/>
     * FULL_MESSAGE - полная строка с обрезанными пробельными символами
     * IS_PRIVATE - приватное сообщение или нет
     * MESSAGE - собственн сообщение, если оно приватное
     * TO_NICK - кому
     * FROM_NICK - от кого
     * TEXT - параметр для получения текстовой части входящего сообщения
     *
     */
    public static final String FULL_MESSAGE = "fMessage";
    public static final String IS_PRIVATE = "private";
    public static final String TO_NICK = "to";
    public static final String MESSAGE = "message";
    public static final String FROM_NICK = "from";
    public static final String TEXT = "text";

    @Resource(name = "chatroomHolder")
    private ChatroomHolder chatroom;

    @OnOpen
    public void openHandler(Session session, @PathParam("nickname") String nickname) {
        session.getUserProperties().put("nickname", nickname);
        chatroom.add(nickname, session);
    }

    @OnClose
    public void closeHandler(Session session) {
        chatroom.remove(getNickname(session));
    }

    @OnError
    public void errorHandler(Throwable th) {
        // TODO: Надо написать обработку ошибки
        th.printStackTrace();
    }

    @OnMessage
    public void messageHandler(String message, Session session) {

        String nickname_from = getNickname(session);

        JsonReader jsonReader = Json.createReader(new StringReader(message));
        String msg = jsonReader.readObject().getString(TEXT);

        Map<String, Object> msgStructure = getMessageStructure(msg);
        if ((boolean) msgStructure.get(IS_PRIVATE)) {
            String to_nick = (String) msgStructure.get(TO_NICK);
            if (chatroom.containsNick(to_nick)) {
                String text = getJSONString(nickname_from, (String) msgStructure.get(MESSAGE), true);
                Session peer = chatroom.getSession(to_nick);
                peer.getAsyncRemote().sendText(text);
            }
        } else {
            List<Session> peers = new ArrayList<>(chatroom.getAllSessions());
            String text = getJSONString(nickname_from, (String) msgStructure.get(FULL_MESSAGE), false);
            for (Session peer : peers) {
                peer.getAsyncRemote().sendText(text);
            }
        }
    }

    private Map<String, Object> getMessageStructure(String msg) {

        Map<String, Object> msgStructure = new HashMap<>();

        Pattern pattern = Pattern.compile("^(private to\\s+){0,1}(\\w+){0,1}\\s*:{0,1}(.*)");
        Matcher matcher = pattern.matcher(msg);

        msgStructure.put(FULL_MESSAGE, msg.trim());
        msgStructure.put(IS_PRIVATE, false);
        msgStructure.put(TO_NICK, "");
        msgStructure.put(MESSAGE, "");

        if (matcher.find()) {

            String tmp = matcher.group(1);
            if (tmp != null)
                msgStructure.put(IS_PRIVATE, tmp.toLowerCase().equals("private to"));

            tmp = matcher.group(2);
            if (tmp != null)
                msgStructure.put(TO_NICK, tmp.trim());

            tmp = matcher.group(3);
            if (tmp != null)
                msgStructure.put(MESSAGE, tmp.trim());

        }

        return msgStructure;
    }

    private String getNickname(Session session) {
        return (String) session.getUserProperties().get("nickname");
    }

    private String getJSONString(String fromUser, String message, boolean isPrivate) {
        StringWriter writer = new StringWriter();
        JsonObjectBuilder builder = Json.createObjectBuilder();
        builder.add(IS_PRIVATE, isPrivate).add(FROM_NICK, fromUser).add(MESSAGE, message);
        try (JsonWriter jsonWriter = Json.createWriter(writer)) {
            jsonWriter.writeObject(builder.build());
        }
        return writer.toString();
    }

}
